/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package guzik.lukasz.snpexplorer;

import SnpDbEntities.Category;
import SnpDbEntities.CodeTypes;
import SnpDbEntities.Snippet;
import SnpDbEntities.User;
import SnpDbEntities.UserPermissions;
import guzik.lukasz.SnpDataAPI.SnpDataAPI;
import guzik.lukasz.SnpLoginEngine.interfaces.LoginEventListener;
import guzik.lukasz.snpexplorer.interfaces.DataReloadRequired;
import guzik.lukasz.snpexplorer.interfaces.SelectedItemListener;
import guzik.lukasz.snpexplorer.interfaces.SnpExplorer;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.logging.Logger;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
//import org.openide.util.ImageUtilities;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.Lookup;

/**
 * Top component which displays something.
 */
@ConvertAsProperties(dtd = "-//guzik.lukasz.snpexplorer//SnpExplorer//EN",
autostore = false)
public final class SnpExplorerTopComponent extends TopComponent implements
	DataReloadRequired, LoginEventListener, SnpExplorer
{
    private static SnpExplorerTopComponent instance;
    private ArrayList<SelectedItemListener> selectedItemListeners = new ArrayList<SelectedItemListener>();
    private TreeDragSource privateTreeDragSource = null;
    private TreeDropTarget privateTreeDropTarget = null;
    private TreeDragSource publicTreeDragSource = null;
    private TreeDropTarget publicTreeDropTarget = null;
    /** path to the icon used by the component and its open action */
//    static final String ICON_PATH = "SET/PATH/TO/ICON/HERE";
    private static final String PREFERRED_ID = "SnpExplorerTopComponent";

    public SnpExplorerTopComponent()
    {
	initComponents();
	setName(NbBundle.getMessage(SnpExplorerTopComponent.class, "CTL_SnpExplorerTopComponent"));
	setToolTipText(NbBundle.getMessage(SnpExplorerTopComponent.class, "HINT_SnpExplorerTopComponent"));
//        setIcon(ImageUtilities.loadImage(ICON_PATH, true));
	putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE);

	jTabPanel.setVisible(false);
    }

    private String getUniqueName(DefaultMutableTreeNode parentNode, String startName)
    {
	String uniqueName = startName;
	int appendix = 1;
	boolean isUniqueName = true;

	if(parentNode.isLeaf())
	{
	    return uniqueName;
	}

	do
	{
	    isUniqueName = true;
	    DefaultMutableTreeNode child = (DefaultMutableTreeNode) parentNode.getFirstChild();
	    do
	    {
		if(child.getUserObject().toString().equals(uniqueName))
		{
		    uniqueName = startName + "_" + appendix;
		    isUniqueName = false;
		    appendix++;
		    break;
		}
		child = (DefaultMutableTreeNode) parentNode.getChildAfter(child);
	    } while(child != null);
	} while(!isUniqueName);

	return uniqueName;
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jTreesPopupMenu = new javax.swing.JPopupMenu();
        jCategoryMenu = new javax.swing.JMenu();
        jAddCategoryMenuItem = new javax.swing.JMenuItem();
        jRemoveCategoryMenuItem = new javax.swing.JMenuItem();
        jSnippetMenu = new javax.swing.JMenu();
        jAddSnippetMenuItem = new javax.swing.JMenuItem();
        jRemoveSnippetMenuItem = new javax.swing.JMenuItem();
        jTreesPopupSeparator = new javax.swing.JPopupMenu.Separator();
        jPublishMenuItem = new javax.swing.JMenuItem();
        jPublishPopupMenu = new javax.swing.JPopupMenu();
        jAcceptPublishingMenuItem = new javax.swing.JMenuItem();
        jDiscardPublishingMenuItem = new javax.swing.JMenuItem();
        jTabPanel = new javax.swing.JTabbedPane();
        jScrollPane1 = new javax.swing.JScrollPane();
        jPrivateTree = new javax.swing.JTree();
        jScrollPane2 = new javax.swing.JScrollPane();
        jPublicTree = new javax.swing.JTree();
        jScrollPane3 = new javax.swing.JScrollPane();
        jPublishQueueTree = new javax.swing.JTree();

        org.openide.awt.Mnemonics.setLocalizedText(jCategoryMenu, org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jCategoryMenu.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(jAddCategoryMenuItem, org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jAddCategoryMenuItem.text")); // NOI18N
        jAddCategoryMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jAddCategoryMenuItemActionPerformed(evt);
            }
        });
        jCategoryMenu.add(jAddCategoryMenuItem);

        org.openide.awt.Mnemonics.setLocalizedText(jRemoveCategoryMenuItem, org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jRemoveCategoryMenuItem.text")); // NOI18N
        jRemoveCategoryMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jRemoveCategoryMenuItemActionPerformed(evt);
            }
        });
        jCategoryMenu.add(jRemoveCategoryMenuItem);

        jTreesPopupMenu.add(jCategoryMenu);

        org.openide.awt.Mnemonics.setLocalizedText(jSnippetMenu, org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jSnippetMenu.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(jAddSnippetMenuItem, org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jAddSnippetMenuItem.text")); // NOI18N
        jAddSnippetMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jAddSnippetMenuItemActionPerformed(evt);
            }
        });
        jSnippetMenu.add(jAddSnippetMenuItem);

        org.openide.awt.Mnemonics.setLocalizedText(jRemoveSnippetMenuItem, org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jRemoveSnippetMenuItem.text")); // NOI18N
        jRemoveSnippetMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jRemoveSnippetMenuItemActionPerformed(evt);
            }
        });
        jSnippetMenu.add(jRemoveSnippetMenuItem);
        jSnippetMenu.add(jTreesPopupSeparator);

        org.openide.awt.Mnemonics.setLocalizedText(jPublishMenuItem, org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jPublishMenuItem.text")); // NOI18N
        jPublishMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jPublishMenuItemActionPerformed(evt);
            }
        });
        jSnippetMenu.add(jPublishMenuItem);

        jTreesPopupMenu.add(jSnippetMenu);

        org.openide.awt.Mnemonics.setLocalizedText(jAcceptPublishingMenuItem, org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jAcceptPublishingMenuItem.text")); // NOI18N
        jAcceptPublishingMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jAcceptPublishingMenuItemActionPerformed(evt);
            }
        });
        jPublishPopupMenu.add(jAcceptPublishingMenuItem);

        org.openide.awt.Mnemonics.setLocalizedText(jDiscardPublishingMenuItem, org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jDiscardPublishingMenuItem.text")); // NOI18N
        jDiscardPublishingMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jDiscardPublishingMenuItemActionPerformed(evt);
            }
        });
        jPublishPopupMenu.add(jDiscardPublishingMenuItem);

        jPrivateTree.setAutoscrolls(true);
        jPrivateTree.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        jPrivateTree.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jPrivateTreeMouseClicked(evt);
            }
            public void mousePressed(java.awt.event.MouseEvent evt) {
                jPrivateTreeMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                jPrivateTreeMouseReleased(evt);
            }
        });
        jScrollPane1.setViewportView(jPrivateTree);

        jTabPanel.addTab(org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jScrollPane1.TabConstraints.tabTitle"), jScrollPane1); // NOI18N

        jPublicTree.setAutoscrolls(true);
        jPublicTree.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        jPublicTree.setLargeModel(true);
        jPublicTree.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jPublicTreeMouseClicked(evt);
            }
            public void mousePressed(java.awt.event.MouseEvent evt) {
                jPublicTreeMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                jPublicTreeMouseReleased(evt);
            }
        });
        jScrollPane2.setViewportView(jPublicTree);

        jTabPanel.addTab(org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jScrollPane2.TabConstraints.tabTitle"), jScrollPane2); // NOI18N

        jPublishQueueTree.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jPublishQueueTreeMouseClicked(evt);
            }
            public void mousePressed(java.awt.event.MouseEvent evt) {
                jPublishQueueTreeMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                jPublishQueueTreeMouseReleased(evt);
            }
        });
        jScrollPane3.setViewportView(jPublishQueueTree);

        jTabPanel.addTab(org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jScrollPane3.TabConstraints.tabTitle"), jScrollPane3); // NOI18N

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jTabPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jTabPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
        );

        jTabPanel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jTabPanel.AccessibleContext.accessibleName")); // NOI18N
    }// </editor-fold>//GEN-END:initComponents

    private void jPrivateTreeMousePressed(java.awt.event.MouseEvent evt)//GEN-FIRST:event_jPrivateTreeMousePressed
    {//GEN-HEADEREND:event_jPrivateTreeMousePressed
	maybyShowTreesPopupMenu(evt);
    }//GEN-LAST:event_jPrivateTreeMousePressed

    private void jPrivateTreeMouseReleased(java.awt.event.MouseEvent evt)//GEN-FIRST:event_jPrivateTreeMouseReleased
    {//GEN-HEADEREND:event_jPrivateTreeMouseReleased
	maybyShowTreesPopupMenu(evt);
    }//GEN-LAST:event_jPrivateTreeMouseReleased

    private void jAddCategoryMenuItemActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jAddCategoryMenuItemActionPerformed
    {//GEN-HEADEREND:event_jAddCategoryMenuItemActionPerformed
	int selectedTabIndex = jTabPanel.getSelectedIndex();
	JTree source = null;
	if(selectedTabIndex == 0)
	{
	    source = jPrivateTree;
	} else if(selectedTabIndex == 1)
	{
	    source = jPublicTree;
	}
	Category newCat = new Category();
	DefaultMutableTreeNode selNode =
		(DefaultMutableTreeNode) source.getLastSelectedPathComponent();

	if(selNode == null)
	{
	    selNode = (DefaultMutableTreeNode) source.getModel().getRoot();
	    newCat.setParentId(0);
	} else
	{
	    newCat.setParentId(((Category) selNode.getUserObject()).getId());
	}
	newCat.setName(getUniqueName(selNode, "Nowa kategoria"));
	newCat.setId(-1);

	int newId = SnpDataAPI.getDefault().addCategory(newCat, selectedTabIndex == 1);
	if(newId == -1)
	{
	    NotifyDescriptor nd = new NotifyDescriptor.Message("Nie udało się dodać kategorii!", NotifyDescriptor.ERROR_MESSAGE);
	    DialogDisplayer.getDefault().notify(nd);
	} else
	{
	    newCat.setId(newId);


	    DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(newCat);
	    ((DefaultTreeModel) source.getModel()).insertNodeInto(newNode, selNode, 0);
	    if(selectedTabIndex == 1)
	    {
		SnpDataAPI.getDefault().getAllPublicCategories().put(newCat.getId(), newCat);
	    } else
	    {
		SnpDataAPI.getDefault().getAllPrivateCategories().put(newCat.getId(), newCat);
	    }

	    TreePath newNodePath = new TreePath(newNode.getPath());
	    source.setSelectionPath(newNodePath);
	    source.startEditingAtPath(newNodePath);
	}
    }//GEN-LAST:event_jAddCategoryMenuItemActionPerformed

    private void jPrivateTreeMouseClicked(java.awt.event.MouseEvent evt)//GEN-FIRST:event_jPrivateTreeMouseClicked
    {//GEN-HEADEREND:event_jPrivateTreeMouseClicked
	if(jPrivateTree.getRowForLocation(evt.getX(), evt.getY()) < 0)
	{
	    jPrivateTree.clearSelection();
	}
    }//GEN-LAST:event_jPrivateTreeMouseClicked

    private void jPublicTreeMouseClicked(java.awt.event.MouseEvent evt)//GEN-FIRST:event_jPublicTreeMouseClicked
    {//GEN-HEADEREND:event_jPublicTreeMouseClicked
	if(jPublicTree.getRowForLocation(evt.getX(), evt.getY()) < 0)
	{
	    jPublicTree.clearSelection();
	}
    }//GEN-LAST:event_jPublicTreeMouseClicked

    private void jRemoveCategoryMenuItemActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jRemoveCategoryMenuItemActionPerformed
    {//GEN-HEADEREND:event_jRemoveCategoryMenuItemActionPerformed
	int selectedTabIndex = jTabPanel.getSelectedIndex();
	JTree source = null;
	if(selectedTabIndex == 0)
	{
	    source = jPrivateTree;
	} else if(selectedTabIndex == 1)
	{
	    source = jPublicTree;
	}
	DefaultMutableTreeNode nodeToDelete = (DefaultMutableTreeNode) source.getLastSelectedPathComponent();
	Category catToRemove =
		(Category) nodeToDelete.getUserObject();

	if(!SnpDataAPI.getDefault().removeCategory(catToRemove.getId(), selectedTabIndex == 1))
	{
	    NotifyDescriptor nd = new NotifyDescriptor.Message("Nie udało się usunąć kategorii!", NotifyDescriptor.ERROR_MESSAGE);
	    DialogDisplayer.getDefault().notify(nd);
	} else
	{
	    DefaultTreeModel model = (DefaultTreeModel) source.getModel();
	    DefaultMutableTreeNode parent = (DefaultMutableTreeNode) nodeToDelete.getParent();

	    DefaultMutableTreeNode child = null;
	    if(nodeToDelete.getChildCount() > 0)
	    {
		child = (DefaultMutableTreeNode) nodeToDelete.getFirstChild();
	    }
	    while(child != null)
	    {
		DefaultMutableTreeNode nextChild = (DefaultMutableTreeNode) nodeToDelete.getChildAfter(child);
		child.removeFromParent();
		model.insertNodeInto(child, parent, parent.getChildCount());

		((Category) child.getUserObject()).setParentId(catToRemove.getParentId());

		child = nextChild;
	    }

	    ((DefaultTreeModel) source.getModel()).removeNodeFromParent(nodeToDelete);
	    if(selectedTabIndex == 1)
	    {
		SnpDataAPI.getDefault().getAllPublicCategories().remove(catToRemove.getId());
	    } else
	    {
		SnpDataAPI.getDefault().getAllPrivateCategories().remove(catToRemove.getId());
	    }
	}
    }//GEN-LAST:event_jRemoveCategoryMenuItemActionPerformed

    private void jAddSnippetMenuItemActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jAddSnippetMenuItemActionPerformed
    {//GEN-HEADEREND:event_jAddSnippetMenuItemActionPerformed
	DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) jPrivateTree.getLastSelectedPathComponent();
	DefaultMutableTreeNode newNode;
	Snippet newSnippet = new Snippet();
	int newId = -1;

	newSnippet.setName(getUniqueName(selNode, "Nowy snippet"));
	newSnippet.setDesc(null);
	newSnippet.setLastModified(new Date());
	newSnippet.setCategoryId(((Category) selNode.getUserObject()).getId());
	newSnippet.setOwnerId(SnpDataAPI.getDefault().getConnectedUser().getId());
	newSnippet.setCodeType(CodeTypes.PLAIN);
	newSnippet.setData("");

	newId = SnpDataAPI.getDefault().addPrivateSnippet(newSnippet);
	if(newId == -1)
	{
	    NotifyDescriptor nd = new NotifyDescriptor.Message("Nie udało się dodać snippetu!", NotifyDescriptor.ERROR_MESSAGE);
	    DialogDisplayer.getDefault().notify(nd);
	    return;
	}
	newSnippet.setId(newId);

	newNode = new DefaultMutableTreeNode(newSnippet);
	((DefaultTreeModel) jPrivateTree.getModel()).insertNodeInto(newNode, selNode, 0);

	TreePath newNodePath = new TreePath(newNode.getPath());
	jPrivateTree.setSelectionPath(newNodePath);
	jPrivateTree.startEditingAtPath(newNodePath);
    }//GEN-LAST:event_jAddSnippetMenuItemActionPerformed

    private void jRemoveSnippetMenuItemActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jRemoveSnippetMenuItemActionPerformed
    {//GEN-HEADEREND:event_jRemoveSnippetMenuItemActionPerformed
	int selectedTabIndex = jTabPanel.getSelectedIndex();
	JTree source = null;
	boolean success;
	if(selectedTabIndex == 0)
	{
	    source = jPrivateTree;
	} else if(selectedTabIndex == 1)
	{
	    source = jPublicTree;
	}
	DefaultMutableTreeNode nodeToDelete = null;
	DefaultTreeModel model = null;
	Snippet snpToDelete = null;
	model = (DefaultTreeModel) source.getModel();
	nodeToDelete = (DefaultMutableTreeNode) source.getLastSelectedPathComponent();
	snpToDelete = (Snippet) nodeToDelete.getUserObject();
	source.setSelectionPath(new TreePath(((DefaultMutableTreeNode) nodeToDelete.getParent()).getPath()));

	if(selectedTabIndex == 1)
	{
	    success = SnpDataAPI.getDefault().unpublish(snpToDelete.getId());
	} else
	{
	    success = SnpDataAPI.getDefault().removePrivateSnippet(snpToDelete.getId());
	}
	if(!success)
	{
	    NotifyDescriptor nd = new NotifyDescriptor.Message("Nie udało się usunąć snippetu!", NotifyDescriptor.ERROR_MESSAGE);
	    DialogDisplayer.getDefault().notify(nd);
	    return;
	}
	if(selectedTabIndex == 1)
	{
	    SnpDataAPI.getDefault().getAllPublicSnippets().remove(snpToDelete.getId());
	} else
	{
	    SnpDataAPI.getDefault().getAllPrivateSnippets().remove(snpToDelete.getId());
	}

	model.removeNodeFromParent(nodeToDelete);
    }//GEN-LAST:event_jRemoveSnippetMenuItemActionPerformed

    private void jPublishMenuItemActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jPublishMenuItemActionPerformed
    {//GEN-HEADEREND:event_jPublishMenuItemActionPerformed
	DefaultMutableTreeNode nodeToPublish;
	Snippet snpToPublish;
	PublishingDialog pd;

	nodeToPublish = (DefaultMutableTreeNode) jPrivateTree.getLastSelectedPathComponent();
	snpToPublish = (Snippet) nodeToPublish.getUserObject();

	pd = new PublishingDialog(jPublicTree, snpToPublish.getId());
	pd.show();
    }//GEN-LAST:event_jPublishMenuItemActionPerformed

    private void jPublishQueueTreeMouseClicked(java.awt.event.MouseEvent evt)//GEN-FIRST:event_jPublishQueueTreeMouseClicked
    {//GEN-HEADEREND:event_jPublishQueueTreeMouseClicked
	if(jPublishQueueTree.getRowForLocation(evt.getX(), evt.getY()) < 0)
	{
	    jPublishQueueTree.clearSelection();
	}
    }//GEN-LAST:event_jPublishQueueTreeMouseClicked

    private void jPublishQueueTreeMousePressed(java.awt.event.MouseEvent evt)//GEN-FIRST:event_jPublishQueueTreeMousePressed
    {//GEN-HEADEREND:event_jPublishQueueTreeMousePressed
	maybyShowPublishPopupMenu(evt);
    }//GEN-LAST:event_jPublishQueueTreeMousePressed

    private void jPublishQueueTreeMouseReleased(java.awt.event.MouseEvent evt)//GEN-FIRST:event_jPublishQueueTreeMouseReleased
    {//GEN-HEADEREND:event_jPublishQueueTreeMouseReleased
	maybyShowPublishPopupMenu(evt);
    }//GEN-LAST:event_jPublishQueueTreeMouseReleased

    private void jAcceptPublishingMenuItemActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jAcceptPublishingMenuItemActionPerformed
    {//GEN-HEADEREND:event_jAcceptPublishingMenuItemActionPerformed
	DefaultMutableTreeNode nodeToDelete = null;
	DefaultTreeModel model = null;
	Snippet snpToPublish = null;
	model = (DefaultTreeModel) jPublishQueueTree.getModel();
	nodeToDelete = (DefaultMutableTreeNode) jPublishQueueTree.getLastSelectedPathComponent();
	snpToPublish = (Snippet) nodeToDelete.getUserObject();

	if(!SnpDataAPI.getDefault().publish(snpToPublish.getId()))
	{
	    NotifyDescriptor nd = new NotifyDescriptor.Message("Nie udało się opublikować snippetu!", NotifyDescriptor.ERROR_MESSAGE);
	    DialogDisplayer.getDefault().notify(nd);
	    return;
	}
	SnpDataAPI.getDefault().getSnippetsToPublish().remove(snpToPublish.getId());
	model.removeNodeFromParent(nodeToDelete);
    }//GEN-LAST:event_jAcceptPublishingMenuItemActionPerformed

    private void jDiscardPublishingMenuItemActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jDiscardPublishingMenuItemActionPerformed
    {//GEN-HEADEREND:event_jDiscardPublishingMenuItemActionPerformed
	DefaultMutableTreeNode nodeToDelete = null;
	DefaultTreeModel model = null;
	Snippet snpToPublish = null;
	model = (DefaultTreeModel) jPublishQueueTree.getModel();
	nodeToDelete = (DefaultMutableTreeNode) jPublishQueueTree.getLastSelectedPathComponent();
	snpToPublish = (Snippet) nodeToDelete.getUserObject();

	if(!SnpDataAPI.getDefault().removeFromPublishQueue(snpToPublish.getId()))
	{
	    NotifyDescriptor nd = new NotifyDescriptor.Message("Nie udało się odrzucić snippetu!", NotifyDescriptor.ERROR_MESSAGE);
	    DialogDisplayer.getDefault().notify(nd);
	    return;
	}
	SnpDataAPI.getDefault().getSnippetsToPublish().remove(snpToPublish.getId());
	model.removeNodeFromParent(nodeToDelete);
    }//GEN-LAST:event_jDiscardPublishingMenuItemActionPerformed

    private void jPublicTreeMousePressed(java.awt.event.MouseEvent evt)//GEN-FIRST:event_jPublicTreeMousePressed
    {//GEN-HEADEREND:event_jPublicTreeMousePressed
	maybyShowTreesPopupMenu(evt);
    }//GEN-LAST:event_jPublicTreeMousePressed

    private void jPublicTreeMouseReleased(java.awt.event.MouseEvent evt)//GEN-FIRST:event_jPublicTreeMouseReleased
    {//GEN-HEADEREND:event_jPublicTreeMouseReleased
	maybyShowTreesPopupMenu(evt);
    }//GEN-LAST:event_jPublicTreeMouseReleased
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenuItem jAcceptPublishingMenuItem;
    private javax.swing.JMenuItem jAddCategoryMenuItem;
    private javax.swing.JMenuItem jAddSnippetMenuItem;
    private javax.swing.JMenu jCategoryMenu;
    private javax.swing.JMenuItem jDiscardPublishingMenuItem;
    private javax.swing.JTree jPrivateTree;
    private javax.swing.JTree jPublicTree;
    private javax.swing.JMenuItem jPublishMenuItem;
    private javax.swing.JPopupMenu jPublishPopupMenu;
    private javax.swing.JTree jPublishQueueTree;
    private javax.swing.JMenuItem jRemoveCategoryMenuItem;
    private javax.swing.JMenuItem jRemoveSnippetMenuItem;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JMenu jSnippetMenu;
    private javax.swing.JTabbedPane jTabPanel;
    private javax.swing.JPopupMenu jTreesPopupMenu;
    private javax.swing.JPopupMenu.Separator jTreesPopupSeparator;
    // End of variables declaration//GEN-END:variables

    /**
     * Gets default instance. Do not use directly: reserved for *.settings files only,
     * i.e. deserialization routines; otherwise you could get a non-deserialized instance.
     * To obtain the singleton instance, use {@link #findInstance}.
     */
    public static synchronized SnpExplorerTopComponent getDefault()
    {
	if(instance == null)
	{
	    instance = new SnpExplorerTopComponent();
	}
	return instance;
    }

    /**
     * Obtain the SnpExplorerTopComponent instance. Never call {@link #getDefault} directly!
     */
    public static synchronized SnpExplorerTopComponent findInstance()
    {
	TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
	if(win == null)
	{
	    Logger.getLogger(SnpExplorerTopComponent.class.getName()).warning(
		    "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system.");
	    return getDefault();
	}
	if(win instanceof SnpExplorerTopComponent)
	{
	    return (SnpExplorerTopComponent) win;
	}
	Logger.getLogger(SnpExplorerTopComponent.class.getName()).warning(
		"There seem to be multiple components with the '" + PREFERRED_ID
		+ "' ID. That is a potential source of errors and unexpected behavior.");
	return getDefault();
    }

    @Override
    public int getPersistenceType()
    {
	return TopComponent.PERSISTENCE_ALWAYS;
    }

    @Override
    public void componentOpened()
    {
	Lookup.getDefault().lookup(guzik.lukasz.SnpLoginEngine.interfaces.LoginEngineSPI.class).getLoginEngine().addLoginEventListener(instance);

	TreeSelectionListener selectionListener = new TreeSelectionListener()
	{
	    @Override
	    public void valueChanged(TreeSelectionEvent e)
	    {
		boolean isPublic = false;
		TreePath path = e.getNewLeadSelectionPath();
		Object selectedItem = null;

		if(path != null)
		{
		    selectedItem = ((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject();
		}

		if(e.getSource() == jPublicTree)
		{
		    isPublic = true;
		}

		if(selectedItem instanceof Snippet)
		{
		    try
		    {
			((Snippet) selectedItem).setData(SnpDataAPI.getDefault().getSnippetWithData(
				((Snippet) selectedItem).getId(), isPublic).getData());
		    } catch(NullPointerException npe)
		    {
			return;
		    }

		    if(e.getSource() == jPublishQueueTree)
		    {
			isPublic = true;
		    }

		    for(SelectedItemListener listener : selectedItemListeners)
		    {
			listener.onSelectedSnippet((Snippet) selectedItem, isPublic);
		    }
		} else //if(selectedItem instanceof Category)
		{
		    for(SelectedItemListener listener : selectedItemListeners)
		    {
			listener.onSelectedCategory((Category) selectedItem, isPublic);
		    }
		}
	    }
	};

	jPrivateTree.addTreeSelectionListener(selectionListener);
	jPublicTree.addTreeSelectionListener(selectionListener);
	jPublishQueueTree.addTreeSelectionListener(selectionListener);

	jPrivateTree.setModel(new ExtendedTreeModel(new DefaultMutableTreeNode("ROOT"), false));
	jPublicTree.setModel(new ExtendedTreeModel(new DefaultMutableTreeNode("ROOT"), true));
	jPublishQueueTree.setModel(new ExtendedTreeModel(new DefaultMutableTreeNode("ROOT"), true));

	jPrivateTree.setCellRenderer(new ExtendedTreeCellRenderer());
	jPublicTree.setCellRenderer(new ExtendedTreeCellRenderer());
	jPublishQueueTree.setCellRenderer(new ExtendedTreeCellRenderer());

	treeDragAndDropSetup();
    }

    @Override
    public void componentClosed()
    {
    }

    void writeProperties(java.util.Properties p)
    {
	// better to version settings since initial version as advocated at
	// http://wiki.apidesign.org/wiki/PropertyFiles
	p.setProperty("version", "1.0");
    }

    Object readProperties(java.util.Properties p)
    {
	if(instance == null)
	{
	    instance = this;
	}
	instance.readPropertiesImpl(p);
	return instance;
    }

    private void readPropertiesImpl(java.util.Properties p)
    {
	String version = p.getProperty("version");
    }

    @Override
    protected String preferredID()
    {
	return PREFERRED_ID;
    }

    @Override
    public void onDataReloadRequired()
    {
	jPrivateTree.setRootVisible(true);
	jPublicTree.setRootVisible(true);

	DefaultTreeModel defTreeModel = (DefaultTreeModel) jPrivateTree.getModel();
	DefaultMutableTreeNode root = new DefaultMutableTreeNode("Pobieranie danych...");
	defTreeModel.setRoot(root);

	defTreeModel = (DefaultTreeModel) jPublicTree.getModel();
	root = new DefaultMutableTreeNode("Pobieranie danych...");
	defTreeModel.setRoot(root);

	ProgressHandle progress = ProgressHandleFactory.createHandle("Pobieranie danych");
	progress.start();
	progress.switchToIndeterminate();

	LinkedHashMap<Integer, Category> privateCategories;
	LinkedHashMap<Integer, Snippet> privateSnippets;
	LinkedHashMap<Integer, Category> publicCategories;
	LinkedHashMap<Integer, Snippet> publicSnippets;
	LinkedHashMap<Integer, Snippet> snippetsToPublish = null;

	privateCategories = SnpDataAPI.getDefault().getAllPrivateCategories();
	privateSnippets = SnpDataAPI.getDefault().getAllPrivateSnippets();

	publicCategories = SnpDataAPI.getDefault().getAllPublicCategories();
	publicSnippets = SnpDataAPI.getDefault().getAllPublicSnippets();

	if(SnpDataAPI.getDefault().getConnectedUser().getPermissions() != UserPermissions.NORMAL_USER)
	{
	    snippetsToPublish = SnpDataAPI.getDefault().getSnippetsToPublish();
	} else
	{
	    snippetsToPublish = new LinkedHashMap<Integer, Snippet>(0);
	}

	progress.setDisplayName("Wypełnianie drzew");

	addRootItemsToTree(jPrivateTree, privateCategories, privateSnippets, true);
	addRootItemsToTree(jPublicTree, publicCategories, publicSnippets, true);
	addRootItemsToTree(jPublishQueueTree, publicCategories, snippetsToPublish, false);

	setupTreesAttributes();

	progress.finish();
    }

    private void addRootItemsToTree(javax.swing.JTree tree,
	    LinkedHashMap<Integer, Category> categories,
	    LinkedHashMap<Integer, Snippet> snippets,
	    boolean addEmpty)
    {

	DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
	DefaultMutableTreeNode root = new DefaultMutableTreeNode("ROOT");
	boolean isntEmpty = false;

	for(Category cat : categories.values())
	{
	    if(cat.getParentId() == 0)
	    {
		DefaultMutableTreeNode catNode = new DefaultMutableTreeNode(cat);
		isntEmpty = addSubItemsToTree(categories, snippets, cat.getId(), model, catNode, addEmpty);

		if(isntEmpty || addEmpty)
		{
		    model.insertNodeInto(catNode, root, 0);
		}
	    }
	}

	model.setRoot(root);
    }

    private boolean addSubItemsToTree(LinkedHashMap<Integer, Category> categories,
	    LinkedHashMap<Integer, Snippet> snippets, Integer actualCat,
	    DefaultTreeModel model, DefaultMutableTreeNode node,
	    boolean addEmpty)
    {
	boolean isntEmpty = false;

	for(Snippet snp : snippets.values())
	{
	    if(snp.getCategoryId() == categories.get(actualCat).getId())
	    {
		model.insertNodeInto(new DefaultMutableTreeNode(snp), node, 0);
		isntEmpty = true;
	    }
	}

	for(Category cat : categories.values())
	{
	    if(cat.getParentId() == categories.get(actualCat).getId())
	    {
		DefaultMutableTreeNode subNode = new DefaultMutableTreeNode(cat);
		if(addSubItemsToTree(categories, snippets, cat.getId(), model, subNode, addEmpty)
			|| addEmpty)
		{
		    isntEmpty = true;
		    model.insertNodeInto(subNode, node, 0);
		}
	    }
	}

	return isntEmpty;
    }

    private void setupTreesAttributes()
    {
	jPrivateTree.expandRow(0);
	jPrivateTree.setRootVisible(false);
	jPrivateTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
	jPrivateTree.setEditable(true);

	jPublicTree.expandRow(0);
	jPublicTree.setRootVisible(false);
	jPublicTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
	jPublicTree.setEditable(false);

	jPublishQueueTree.expandRow(0);
	jPublishQueueTree.setRootVisible(false);
	jPublishQueueTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
	jPublishQueueTree.setEditable(false);
    }

    @Override
    public void onLogin()
    {
	User connectedUser = SnpDataAPI.getDefault().getConnectedUser();

	jTabPanel.setVisible(true);
	onDataReloadRequired();
	treeDragAndDropSetup();

	int indexOfPublishTab = jTabPanel.indexOfTab(org.openide.util.NbBundle.getMessage(SnpExplorerTopComponent.class, "SnpExplorerTopComponent.jScrollPane3.TabConstraints.tabTitle"));
	if(connectedUser.getPermissions() == UserPermissions.NORMAL_USER)
	{
	    jTabPanel.setEnabledAt(indexOfPublishTab, false);
	} else
	{
	    jTabPanel.setEnabledAt(indexOfPublishTab, true);
	}

	if(connectedUser.getPermissions() == UserPermissions.SUPER_ADMIN
		|| connectedUser.getPermissions() == UserPermissions.ADMIN
		|| connectedUser.getPermissions() == UserPermissions.MODERATOR)
	{
	    jPublicTree.setEditable(true);
	} else
	{
	    jPublicTree.setEditable(false);
	}
    }

    @Override
    public void onLogout()
    {
	jTabPanel.setVisible(false);
	jPrivateTree.clearSelection();
	jPublicTree.clearSelection();
	jPublishQueueTree.clearSelection();
	treeDragAndDropSetup();
    }

    @Override
    public void addSelectedItemListener(SelectedItemListener listener)
    {
	selectedItemListeners.add(listener);
    }

    private void maybyShowTreesPopupMenu(MouseEvent evt)
    {
	JTree source = (JTree) evt.getSource();
	boolean isPublic = source == jPublicTree;
	User connectedUser = SnpDataAPI.getDefault().getConnectedUser();

	if(evt.isPopupTrigger())
	{
	    if(isPublic
		    && connectedUser.getPermissions() == UserPermissions.NORMAL_USER)
	    {
		return;
	    }

	    TreePath selectionPath = source.getSelectionPath();
	    if(selectionPath == null)
	    {
		if(isPublic
			&& connectedUser.getPermissions() == UserPermissions.MODERATOR)
		{
		    return;
		}

		jCategoryMenu.setEnabled(true);
		jRemoveCategoryMenuItem.setEnabled(false);
		jSnippetMenu.setEnabled(false);
	    } else
	    {
		Object selectedItem =
			((DefaultMutableTreeNode) selectionPath.getLastPathComponent()).getUserObject();


		if(selectedItem instanceof Category)
		{
		    if(isPublic
			    && connectedUser.getPermissions() == UserPermissions.MODERATOR)
		    {
			return;
		    }
		    jCategoryMenu.setEnabled(true);
		    jRemoveCategoryMenuItem.setEnabled(true);
		    jSnippetMenu.setEnabled(true);
		    jAddSnippetMenuItem.setEnabled(true);
		    jPublishMenuItem.setEnabled(false);
		    jRemoveSnippetMenuItem.setEnabled(false);
		} else if(selectedItem instanceof Snippet)
		{
		    if(isPublic
			    && connectedUser.getPermissions() == UserPermissions.MODERATOR)
		    {
			ArrayList<Integer> moderators;
			moderators = SnpDataAPI.getDefault().getCategoryModerators(((Snippet)selectedItem).getCategoryId());
			if(!moderators.contains(connectedUser.getId()))
			{
			    return;
			}
		    }
		    
		    jCategoryMenu.setEnabled(false);
		    jSnippetMenu.setEnabled(true);
		    jAddSnippetMenuItem.setEnabled(false);
		    jRemoveSnippetMenuItem.setEnabled(true);
		    jPublishMenuItem.setEnabled(true);
		}
	    }

	    jAddSnippetMenuItem.setVisible(!isPublic);
	    jPublishMenuItem.setVisible(!isPublic);
	    jTreesPopupSeparator.setVisible(!isPublic);

	    jTreesPopupMenu.show(evt.getComponent(), evt.getX(), evt.getY());
	}
    }

    private void maybyShowPublishPopupMenu(MouseEvent evt)
    {
	if(evt.isPopupTrigger())
	{
	    TreePath selectionPath = jPublishQueueTree.getSelectionPath();
	    if(selectionPath != null)
	    {
		Object selectedItem =
			((DefaultMutableTreeNode) selectionPath.getLastPathComponent()).getUserObject();

		if(selectedItem instanceof Snippet)
		{
		    jAcceptPublishingMenuItem.setEnabled(true);
		    jDiscardPublishingMenuItem.setEnabled(true);
		    jPublishPopupMenu.show(evt.getComponent(), evt.getX(), evt.getY());
		}
	    }

	}
    }

    private void treeDragAndDropSetup()
    {
	User connectedUser = SnpDataAPI.getDefault().getConnectedUser();
	if(privateTreeDragSource == null)
	{
	    privateTreeDragSource = new TreeDragSource(jPrivateTree);
	    privateTreeDropTarget = new TreeDropTarget(jPrivateTree, false);
	    publicTreeDragSource = new TreeDragSource(jPublicTree);
	    publicTreeDropTarget = new TreeDropTarget(jPublicTree, true);
	}

	if(connectedUser == null
		|| connectedUser.getPermissions() == UserPermissions.NORMAL_USER
		|| connectedUser.getPermissions() == UserPermissions.MODERATOR)
	{
	    publicTreeDragSource.setEnabled(false);
	    publicTreeDropTarget.setEnabled(false);
	} else
	{
	    publicTreeDragSource.setEnabled(true);
	    publicTreeDropTarget.setEnabled(true);
	}
    }
}
